package com.fourfinance.model.service.impl;

import java.util.Date;
import java.util.List;
import java.util.Set;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.fourfinance.exception.LenderException;
import com.fourfinance.model.dao.ClientDAO;
import com.fourfinance.model.dao.IpDAO;
import com.fourfinance.model.dao.LoanDAO;
import com.fourfinance.model.domain.Client;
import com.fourfinance.model.domain.Ip;
import com.fourfinance.model.domain.Loan;
import com.fourfinance.model.service.ClientService;
import com.fourfinance.util.LenderConstants;
import com.fourfinance.util.LenderUtil;
import com.fourfinance.validation.LenderAppValidator;

@Service
public class ClientServiceImpl implements ClientService {

	@Autowired
	private ClientDAO clientDAO;
	
	@Autowired
	private LoanDAO loanDAO;
	
	@Autowired
	private IpDAO ipDAO;	
	
	@Transactional
	public Long add(Client client) {
		return clientDAO.add(client);
	}

	@Transactional
	public List<Client> list() {
		return clientDAO.list();
	}

	@Transactional
	public void remove(Long id) {
		clientDAO.remove(id);
	}
	
	@Transactional
	public void removeAll() {
		clientDAO.removeAll();
	}

	@Transactional
	public Client get(Long id) {
		return clientDAO.get(id);
	}

	@Transactional
	public Client getByEmail(String email) {
		return clientDAO.getByEmail(email);
	}	

	@Transactional
	public Client getByFirstname(String firstName) {
		return clientDAO.getByFirstname(firstName);
	}

	@Transactional
	public Loan applyLoan(Client client, String ipAddress, Long amount, Date term) throws LenderException {
		
		if(!LenderAppValidator.isTimingAllowed(new Date())){
			throw new LenderException(LenderUtil.getMessage("rejection.bad.timing", LenderUtil.getMinTime(), LenderUtil.getMaxTime()));
		}
		
		if(!LenderAppValidator.isAmountAllowed(amount)){
			throw new LenderException(LenderUtil.getMessage("rejection.bad.amount", LenderConstants.MAX_AMOUNT));
		}
		
		Client clientTmp = clientDAO.getByEmail(client.getEmail());
		if(clientTmp == null){
			Long id = clientDAO.add(client);
			clientTmp = clientDAO.get(id);
		}
		
		Ip ip = ipDAO.getByIpAddress(ipAddress);		
		if(ip == null){
			ip = new Ip();
			ip.setIpAddress(ipAddress);
			ip.setClient(clientTmp);
			ip.setLastAttempt(new Date());
			ip.setAttemptCount(1);
			ipDAO.add(ip);
		} else {
			ip.setAttemptCount(ip.getAttemptCount() + 1);			
			ip.setLastAttempt(new Date()); //Current time
			ipDAO.update(ip);
			if(!LenderAppValidator.isAttemptAllowed(ip)){
				throw new LenderException(LenderUtil.getMessage("rejection.bad.attemptno", LenderConstants.MAX_ATTEMPT));
			}			
		}
		
		Loan loan = new Loan();
		loan.setAmount(amount);
		loan.setClient(clientTmp);
		loan.setExtendedCount(0);
		loan.setInterest(LenderConstants.DEFAULT_INTEREST);
		loan.setTerm(term);
		loan.setCreatedDate(new Date());
		loan.setReferenceId(LenderUtil.generateReference());
		Long id = loanDAO.add(loan);
		return loanDAO.get(id);
		
	}

	@Transactional
	public Loan extendLoan(String referenceId) throws LenderException {
		
		Loan loan = loanDAO.getByReferenceId(referenceId);
		if(loan == null){
			throw new LenderException(LenderUtil.getMessage("cannot.find.loan"));
		}
		
		Date newTerm = LenderUtil.extendTerm(loan.getTerm());		
		Float newInterest = LenderUtil.getExtensionInterest(loan.getInterest());		
		Integer newCount = loan.getExtendedCount() + 1;
		
		loan.setTerm(newTerm);
		loan.setInterest(newInterest);
		loan.setExtendedCount(newCount);
		loanDAO.update(loan);
		
		return loanDAO.getByReferenceId(referenceId);
		
	}

	@Transactional
	public Set<Loan> listLoans(Client client) {
		Set<Loan> loans = null;
		Client clientInDB = clientDAO.getByEmail(client.getEmail());
		if(clientInDB != null){
			loans = clientInDB.getLoans();
		}
		return loans;
	}

	@Transactional
	public void remove(String email) {
		clientDAO.remove(email);		
	}
	
}
